/*
 * Filename: d:\idf_workspace\esp_face_id\main\src\app_uvc.cc
 * Path: d:\idf_workspace\esp_face_id\main\src
 * Created Date: Friday, July 25th 2025, 11:50:42 am
 * Author: sss
 *
 * Copyright (c) 2025 Your Company
 */
#include "app_uvc.h"
#include "esp_camera.h"
#include "usb_device_uvc.h"
#include "uvc_frame_config.h"
#include "esp_log.h"
#include "esp_err.h"
#include "assert.h"
#include "time.h"
#include <stdint.h>
#include "esp_jpeg_enc.h"

const static char *TAG = "AppUvc";
static QueueHandle_t uvc_queue = nullptr;
#define UVC_MAX_FRAMESIZE_SIZE (150 * 1024)

struct timeval tv_now;


uint8_t *rgb888_buf = NULL;
uint8_t *outbuf = NULL;
int outbuf_size = 100 * 1024;
int out_len = 0;
jpeg_enc_handle_t jpeg_enc = NULL;
int jpeg_width = 320;
int jpeg_height = 240;
int image_size = jpeg_width * jpeg_height * 3;

jpeg_error_t ret = JPEG_ERR_OK;

static void rgb565_to_rgb888(uint16_t *src, uint8_t *dst, uint32_t pixel_count)
{
    for (uint32_t i = 0; i < pixel_count; i++)
    {
        // uint16_t pixel = src[i];
        uint16_t pixel = (src[i] << 8) | (src[i] >> 8); // 字节交换

        // 提取并扩展红色分量
        dst[i * 3] = ((pixel >> 11) & 0x1F) << 3; // R5 → R8
        dst[i * 3] |= dst[i * 3] >> 5;            // 填充低位

        // 提取并扩展绿色分量
        dst[i * 3 + 1] = ((pixel >> 5) & 0x3F) << 2; // G6 → G8
        dst[i * 3 + 1] |= dst[i * 3 + 1] >> 6;       // 填充低位

        // 提取并扩展蓝色分量
        dst[i * 3 + 2] = (pixel & 0x1F) << 3;  // B5 → B8
        dst[i * 3 + 2] |= dst[i * 3 + 2] >> 5; // 填充低位
    }
}

typedef struct
{
    camera_fb_t *cam_fb_p;
    uvc_fb_t uvc_fb;
} fb_t;

static fb_t s_fb;

static esp_err_t camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx)
{
    ESP_LOGI(TAG, "camera_start_cb width %d height %d rate %d", width, height, rate);
    return ESP_OK;
}
static uvc_fb_t *camera_fb_get_cb(void *cb_ctx)
{

    // ESP_LOGI(TAG, "camera_fb_get_cb");

    (void)cb_ctx;
    // s_fb.cam_fb_p = esp_camera_fb_get();

    // ESP_LOGI(TAG, "camera_fb_get_cb ");

    xQueueReceive(uvc_queue, &s_fb.cam_fb_p, portMAX_DELAY);

    if (!s_fb.cam_fb_p)
    {
        ESP_LOGI(TAG, "No frame available");
        return NULL;
    }

    

    rgb565_to_rgb888((uint16_t *)s_fb.cam_fb_p->buf, rgb888_buf, s_fb.cam_fb_p->len / 2);
    // gettimeofday(&tv_now, NULL);
    // int64_t time_us_start = (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;

    ret = jpeg_enc_process(jpeg_enc, rgb888_buf, image_size, outbuf, outbuf_size, &out_len);

    // gettimeofday(&tv_now, NULL);
    // int64_t time_us_end = (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;

    // ESP_LOGI(TAG, "JPEG encode time %lld us", time_us_end - time_us_start);
    
    if (ret != JPEG_ERR_OK)
    {
        ESP_LOGE(TAG, "JPEG encode failed with error %d", ret);
        esp_camera_fb_return(s_fb.cam_fb_p);
        return NULL;
    }
    else
    {

        // extern const uint8_t assets_testuvc_jpg_start[] asm("_binary_testuvc_jpg_start");
        // extern const uint8_t assets_testuvc_jpg_end[]   asm("_binary_testuvc_jpg_end");
        // size_t len = assets_testuvc_jpg_end - assets_testuvc_jpg_start;

        ESP_LOGI(TAG, "JPEG encode success outlen %d",out_len);
        // s_fb.uvc_fb.buf = const_cast<uint8_t*>(assets_testuvc_jpg_start);
        s_fb.uvc_fb.buf = outbuf;
        s_fb.uvc_fb.len = out_len;
        s_fb.uvc_fb.width = s_fb.cam_fb_p->width;
        s_fb.uvc_fb.height = s_fb.cam_fb_p->height;
        s_fb.uvc_fb.format = UVC_FORMAT_JPEG;
        s_fb.uvc_fb.timestamp = s_fb.cam_fb_p->timestamp;
        return &s_fb.uvc_fb;
    }

    // s_fb.uvc_fb.buf = s_fb.cam_fb_p->buf;
    // s_fb.uvc_fb.len = s_fb.cam_fb_p->len;
    // s_fb.uvc_fb.width = s_fb.cam_fb_p->width;
    // s_fb.uvc_fb.height = s_fb.cam_fb_p->height;
    // s_fb.uvc_fb.format = UVC_FORMAT_JPEG;
    // s_fb.uvc_fb.timestamp = s_fb.cam_fb_p->timestamp;

    // if (s_fb.uvc_fb.len > UVC_MAX_FRAMESIZE_SIZE)
    // {
    //     ESP_LOGE(TAG, "Frame size %d is larger than max frame size %d", s_fb.uvc_fb.len, UVC_MAX_FRAMESIZE_SIZE);
    //     esp_camera_fb_return(s_fb.cam_fb_p);
    //     return NULL;
    // }
    // return &s_fb.uvc_fb;
}

static void camera_fb_return_cb(uvc_fb_t *fb, void *cb_ctx)
{
    (void)cb_ctx;
    // assert(fb == &s_fb.uvc_fb);
    esp_camera_fb_return(s_fb.cam_fb_p);
}
static void camera_stop_cb(void *cb_ctx)
{
}

AppUvc::AppUvc(QueueHandle_t in_queue)
{
    uvc_queue = in_queue;
}
AppUvc::~AppUvc()
{
    uvc_queue = nullptr;
    jpeg_enc_close(jpeg_enc);
    free(outbuf);
    free(rgb888_buf);
}

void AppUvc::start()
{
    ESP_LOGI(TAG, "Starting UVC camera");
    // camera_fb_t *fb = nullptr;
    const size_t buff_size = 80 * 1024;
    uint8_t *uvc_buffer = (uint8_t *)malloc(buff_size);
    assert(uvc_buffer != NULL);

    uvc_device_config_t config = {
        .uvc_buffer = uvc_buffer,
        .uvc_buffer_size = buff_size,
        .start_cb = camera_start_cb,
        .fb_get_cb = camera_fb_get_cb,
        .fb_return_cb = camera_fb_return_cb,
        .stop_cb = camera_stop_cb,
        .cb_ctx = NULL,
    };

    ESP_ERROR_CHECK(uvc_device_config(0, &config));
    ESP_ERROR_CHECK(uvc_device_init());

    jpeg_enc_config_t jpeg_enc_cfg = DEFAULT_JPEG_ENC_CONFIG();
    jpeg_enc_cfg.width = jpeg_width;
    jpeg_enc_cfg.height = jpeg_height;
    jpeg_enc_cfg.src_type = JPEG_PIXEL_FORMAT_RGB888;
    jpeg_enc_cfg.subsampling = JPEG_SUBSAMPLE_422;
    jpeg_enc_cfg.quality = 60;
    jpeg_enc_cfg.rotate = JPEG_ROTATE_0D;
    jpeg_enc_cfg.task_enable = true;
    jpeg_enc_cfg.hfm_task_priority = 5;
    jpeg_enc_cfg.hfm_task_core = 1;

    ret = jpeg_enc_open(&jpeg_enc_cfg, &jpeg_enc);
    if (ret != JPEG_ERR_OK)
    {
        ESP_LOGE(TAG, "jpeg 解码初始化失败");
    }
    outbuf = (uint8_t *)calloc(1, outbuf_size);
    assert(outbuf != NULL);
    rgb888_buf = (uint8_t *)malloc(jpeg_width * jpeg_height * 3);
    // while (true)
    // {
    //     // read from in_queue and process the data
    //     xQueueReceive(in_queue, &fb, portMAX_DELAY);
    // }
}